package com.icesoft.base.manager.base;

import com.icesoft.base.manager.config.UrlConstant;
import com.icesoft.core.common.exception.CheckException;
import com.icesoft.core.common.helper.Resp;
import com.icesoft.core.common.helper.RespCode;
import com.icesoft.core.dao.base.BaseModel;
import com.icesoft.core.web.helper.PathUtil;
import com.icesoft.core.web.helper.ResponseUtils;
import com.icesoft.core.web.helper.excel.ExcelUtil;
import com.icesoft.core.web.helper.excel.ExcelUtil.CellHandle;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.UnsupportedFileFormatException;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Excel导入Base控制器
 *
 * @author XHH
 */
public abstract class BaseExcelController<T extends BaseModel, S extends BaseService<T>> {
    private static final String FAILURE_FOLD = "faliure/";
    protected final Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private S service;

    public final S getService() {
        return service;
    }

    /**
     * 是否存在模板文件接口
     *
     * @return Resp
     */
    @GetMapping("existTemplate")
    @ResponseBody
    public Resp<Boolean> existTemplate() {
        // 判断模板文件是否存在
        String fileName = appendExt(getTemplateFileName());
        File templateFile = new File(getExcelRootPath(), fileName);
        boolean existTemp = templateFile.exists();
        return Resp.success(existTemp);
    }

    public abstract BaseImportAction<T> getImportAction(HttpServletRequest req);

    /**
     * Excel上传接口,处理Excel上传页面的post请求
     *
     * @param model
     * @param excel   excel文件
     * @param request
     * @return
     * @throws IOException Resp<?>
     * @Description
     */
    @PostMapping(value = UrlConstant.IMP)
    @ResponseBody
    public Resp<?> impMapping(T model, @RequestParam(name = "excel", required = true) MultipartFile excel,
                              HttpServletRequest request) throws IOException {
        Map<String, String[]> paramMap = request.getParameterMap();
        log.debug("paramMap:{}", paramMap);
        if (excel.isEmpty()) {
            throw new CheckException("文件不能为空");
        }
        String originalFilename = excel.getOriginalFilename();
        String name = excel.getName();
        // multipartFile参数接收
        log.debug("originalFilename:{}", originalFilename);
        log.debug("name:{}", name);
        String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
        if (!".xlsx".equals(fileType) && !".xls".equals(fileType)) {
            throw new CheckException("excel格式错误");
        }
        byte[] bytes = excel.getBytes();
        String excelRootPath = getExcelRootPath();
        String savePath = excelRootPath + "imp/" + UUID.randomUUID().toString() + fileType;// 文件上传路径
        FileUtils.writeByteArrayToFile(new File(savePath), bytes);
        BaseImportAction<T> importAction = getImportAction(request);
        List<Map<String, String>> failures = new ArrayList<>();
        try {
            List<T> models = importAction.readExcel(savePath, failures);
            if (models.isEmpty() && failures.isEmpty()) {
                return Resp.error("未读到excel数据，请检查excel是否为空");
            }
            impCreate(models);
            log.info("总共读到 " + models.size() + " 条数据");
        } catch (UnsupportedFileFormatException e) {
            log.error("文件后缀格式错误", e);
            return Resp.error("文件后缀格式错误");
        } catch (IllegalArgumentException e) {
            log.error("数据校验失败", e);
            return Resp.error(e.getMessage());
        }
        Resp<String> jr = new Resp<>();
        int size = failures.size();
        if (size > 0) {
            log.info("有 " + size + " 条数据导入失败");
            String uuid = UUID.randomUUID().toString();
            String outPath = excelRootPath + FAILURE_FOLD + uuid + ".xls";
            log.debug("导入失败excel保存路径：{}", outPath);
            BaseImportAction<T> finalImportAction = importAction;
            ExcelUtil.writeToExcel(failures, outPath, new CellHandle() {
                @Override
                public void cell(String title, String value, Cell cell, Workbook wb) {
                    if (finalImportAction.errorTitle().equals(title)) {
                        CellStyle style = wb.createCellStyle();
                        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                        style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
                        cell.setCellStyle(style);
                    }
                }

            });
            jr.setData(uuid);
            jr.setMsg("有 " + size + " 条数据导入失败");
            jr.setCode(RespCode.REDIRECT);
            return jr;
        } else {
            jr.setMsg("所有数据导入成功");
            return jr;
        }
    }

    /**
     * Excel上传失败反馈文件下载
     *
     * @param response
     * @param fileName void
     * @Description
     */
    @GetMapping("downloadErrorExcel")
    public void downloadExcel(HttpServletResponse response, @RequestParam String fileName) {
        String path = getExcelRootPath() + FAILURE_FOLD + fileName + ".xls";
        File excel = new File(path);
        if (!excel.exists()) {
            ResponseUtils.writeJson(response, Resp.error("文件不存在"));
            return;
        }
        try {
            ResponseUtils.writeToFileStream(response, appendExt(getErrorExcelDownloadName()),
                    new FileInputStream(excel));
        } catch (IOException e) {
            ResponseUtils.writeJson(response, Resp.error("文件读取错误"));
        }

    }

    /**
     * 下载Excel上传用模板文件
     *
     * @param response void
     * @Description
     */
    @GetMapping("downloadTemplate")
    public void downloadTemplate(HttpServletResponse response) {
        String fileName = appendExt(getTemplateFileName());
        if (!fileName.endsWith(".xls")) {
            ResponseUtils.writeJson(response, Resp.error("模板文件名错误，必须以.xls结尾"));
            return;
        }
        File templateExcel = new File(getExcelRootPath(), fileName);
        log.info("模板文件路径:{}", templateExcel.getAbsolutePath());
        if (!templateExcel.exists()) {
            ResponseUtils.writeJson(response, Resp.error("模板文件不存在 <br>请选择模板文件后点击【上传模板】按钮上传模板文件"));
            return;
        }
        try {
            ResponseUtils.writeToFileStream(response, fileName, new FileInputStream(templateExcel));
        } catch (IOException e) {
            ResponseUtils.writeJson(response, Resp.error("文件读取错误"));
            return;
        }

    }

    /**
     * 上传模板excel
     *
     * @param excel    模板文件MultipartFile
     * @param response
     * @throws IOException
     */
    @PostMapping("uploadTemplete")
    public void uploadTemplete(MultipartFile excel, HttpServletResponse response) {
        String fileName = appendExt(getTemplateFileName());
		/*if (!fileName.endsWith(".xls")) {
			ResponseUtils.writeJson(response, Resp.error("模板文件名错误，必须以.xls结尾"));
			return;
		}*/
        File templateFile = new File(getExcelRootPath(), fileName);
        log.info("上传模板文件路径:{}", templateFile.getAbsolutePath());
        try {
            excel.transferTo(templateFile);
        } catch (IOException i) {
            ResponseUtils.writeJson(response, Resp.error("文件读写错误"));
            return;
        } catch (IllegalStateException s) {
            ResponseUtils.writeJson(response, Resp.error("文件已被移动，请重新上传"));
            return;
        }
        ResponseUtils.writeJson(response, Resp.success("文件已上传").msg("模板上传成功"));
    }

    public void impCreate(List<T> models) {
        getService().create(models);
    }

    private String appendExt(String fileName) {
        if (!StringUtils.contains(fileName, ".")) {
            fileName += ".xls";
        }
        return fileName;
    }

    /**
     * 上传模板文件名
     */
    public abstract String getTemplateFileName();

    /**
     * 导入失败数据文件名
     */
    public String getErrorExcelDownloadName() {
        return "导入失败数据";
    }

    public final String getExcelRootPath() {
        String excelRootPath = PathUtil.getRootUploadPath() + "excel/";
        File excelDirectory = new File(excelRootPath);
        if (!excelDirectory.exists()) {
            excelDirectory.mkdirs();
        }
        return excelRootPath;
    }
}
